﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace Elca.MvvmHelpers {

    public class ViewModelCreator {

        public static readonly DependencyProperty DataContextProperty = 
            DependencyProperty.RegisterAttached(
              "DataContext",
              typeof(object),
              typeof(ViewModelCreator),
              new PropertyMetadata(null, OnDataContextChanged)
            );


        public static void SetDataContext(DependencyObject element, object value) {
            element.SetValue(DataContextProperty, value);
        }
        public static object GetDataContext(DependencyObject element) {
            return element.GetValue(DataContextProperty);
        }

        private static void OnDataContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {

            try {
                FrameworkElement fe = d as FrameworkElement;
                if (fe != null) {

                    Type toBeCreated = e.NewValue as Type;
                    if (toBeCreated == null) {

                        string typeName = e.NewValue as string;
                        if (! String.IsNullOrEmpty(typeName)) {

                            try {
                                toBeCreated = Type.GetType(typeName, true, true);
                            } catch (TypeLoadException) {

                                Assembly source = d.GetType().Assembly;
                                toBeCreated = source.GetType(typeName);
                            }
                        }
                    }

                    object vm = toBeCreated != null ? Activator.CreateInstance(toBeCreated) : null;

                    // If it's a ViewModel type, and the target is a top-level Window, then subscribe to the close request
                    // event and close the window if the view model raises the event.
                    BaseViewModel bvm = vm as BaseViewModel;
                    if (bvm != null) {

                        Window winTarget = d as Window;

                        if (winTarget != null) {
                            bvm.CloseRequest += ((s, ea) => {
                                                     try {
                                                         winTarget.DialogResult = ea.Result;
                                                     } catch (InvalidOperationException) {
                                                         // Raised if this was displayed via Show() vs. ShowDialog
                                                         winTarget.Close();
                                                     }
                                                 });
                            bvm.ActivateRequest += ((s, ea) => winTarget.Activate());

                            BaseViewModel.IsInDesignMode = DesignerProperties.GetIsInDesignMode(winTarget);
                        }
                    }
                    fe.DataContext = vm;
                }
            } catch {
                // If we are in design mode, then don't allow the exception to propogate out -- it kills the design surface.
                if ( (bool)
                     (DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue))
                    return;

                // Otherwise throw it
                throw;
            }
        }
    }
}
